package xxl.ioc.mapping 
{
	import xxl.ioc.dependencyproviders.ClassProvider;
	import xxl.ioc.dependencyproviders.ForwardingProvider;
	import xxl.ioc.dependencyproviders.IDependencyProvider;
	import xxl.ioc.dependencyproviders.InjectorUsingProvider;
	import xxl.ioc.dependencyproviders.LocalOnlyProvider;
	import xxl.ioc.dependencyproviders.SingletonProvider;
	import xxl.ioc.dependencyproviders.SoftDependencyProvider;
	import xxl.ioc.dependencyproviders.ValueProvider;
	import xxl.ioc.Injector;
	import xxl.ioc.signals.MappingSignal;
	import xxl.ioc.utils.IOCInternal;
	/**
	 * ...
	 * @author XXL
	 */
	public class InjectionMapping implements IProviderlessMapping, IUnsealedMapping 
	{
		private var _creatingInjector:Injector;
		private var _type:Class;
		private var _name:String;
		private var _mappingId:String;
		private var _defaultProviderSet:Boolean;
		
		private var _overridingInjector:Injector;
		private var _soft:Boolean = false;
		private var _local:Boolean = false;
		private var _sealed:Boolean = false;
		private var _sealKey:Object = null;
		
		public function InjectionMapping(creatingInjector:Injector, type:Class, name:String, mappingId:String) 
		{
			_creatingInjector = creatingInjector;
			_type = type;
			_name = name;
			_mappingId = mappingId;
			_defaultProviderSet = true;
			mapProvider(new ClassProvider(type));
		}
		
		public function unseal(key:Object):InjectionMapping
		{
			if (!_sealed)
			{
				throw new Error("尚未seal");
			}
			if (key !== _sealKey)
			{
				throw new Error("sealkey不正确");
			}
			_sealed = false;
			_sealKey = null;
			return this;
		}
		
		public function get isSealed():Boolean
		{
			return _sealed;
		}
		
		public function hasProvider():Boolean
		{
			return Boolean(_creatingInjector.IOCInternal::providerMappings[_mappingId]);
		}
		
		public function softly():IProviderlessMapping
		{
			_sealed && throwSealedError();
			if (!_soft)
			{
				const provider:IDependencyProvider = getProvider();
				dispatchPreChange();
				_soft = true;
				mapProvider(provider);
				dispatchPostChange();
			}
			return this;
		}
		
		public function getProvider():IDependencyProvider
		{
			var provider:IDependencyProvider = _creatingInjector.IOCInternal::providerMappings[_mappingId];
			while (provider is ForwardingProvider)
			{
				provider = ForwardingProvider(provider).provider;
			}
			return provider;
		}
		
		public function locally():IProviderlessMapping
		{
			_sealed && throwSealedError();
			if (_local)
			{
				return this;
			}
			const provider:IDependencyProvider = getProvider();
			dispatchPreChange();
			_local = true;
			mapProvider(provider);
			dispatchPostChange();
			return this;
		}
		
		public function setInjector(injector:Injector):InjectionMapping
		{
			_sealed && throwSealedError();
			if (injector == _overridingInjector)
			{
				return this;
			}
			const provider:IDependencyProvider = getProvider();
			_overridingInjector = injector;
			mapProvider(provider);
			return this;
		}
		
		//----------private/protected
		
		private function mapProvider(provider:IDependencyProvider):void
		{
			if (_soft)
			{
				provider = new SoftDependencyProvider(provider);
			}
			if (_local)
			{
				provider = new LocalOnlyProvider(provider);
			}
			if (_overridingInjector)
			{
				provider = new InjectorUsingProvider(_overridingInjector, provider);
			}
			_creatingInjector.IOCInternal::providerMappings[_mappingId] = provider;
		}
		
		private function throwSealedError():void
		{
			throw new Error("不能更改sealed的mapping");
		}
		
		[Inline]
		final private function dispatchPreChange():void
		{
			_creatingInjector.mappingSignal.dispatch(MappingSignal.PRE_MAPPING_CHANGE, _type, _name, this);
		}
		
		[Inline]
		final private function dispatchPostChange():void
		{
			_creatingInjector.mappingSignal.dispatch(MappingSignal.POST_MAPPING_CHANGE, _type, _name, this);
		}
		
		/* INTERFACE xxl.ioc.mapping.IProviderlessMapping */
		
		public function toType(type:Class):IUnsealedMapping 
		{
			toProvider(new ClassProvider(type));
			return this;
		}
		
		public function toValue(value:Object):IUnsealedMapping 
		{
			toProvider(new ValueProvider(value));
			return this;
		}
		
		public function toSingleton(type:Class):IUnsealedMapping 
		{
			toProvider(new SingletonProvider(type, _creatingInjector));
			return this;
		}
		
		public function asSingleton():IUnsealedMapping 
		{
			toSingleton(_type);
			return this;
		}
		
		public function toProvider(provider:IDependencyProvider):IUnsealedMapping 
		{
			_sealed && throwSealedError();
			if (hasProvider() && provider != null && !_defaultProviderSet)
			{
				trace("Injector already has a mapping for " + _mappingId);
				_creatingInjector.mappingSignal.dispatchFun(MappingSignal.MAPPING_OVERRIDE, _type, _name, this);
			}
			dispatchPreChange();
			_defaultProviderSet = false;
			mapProvider(provider);
			dispatchPostChange();
			return this;
		}
		
		public function seal():Object 
		{
			if (_sealed)
			{
				throw new Error("Mapping已经sealed了");
			}
			_sealed = true;
			_sealKey = { };
			return _sealKey;
		}
		
	}

}